
package org.spiderflow.core.expression;

import org.spiderflow.core.exception.TemplateException;
import org.spiderflow.core.expression.parsing.Span;
import org.spiderflow.core.expression.parsing.Span.Line;
import org.spiderflow.core.expression.parsing.token.TokenStream;

/**
 * All errors reported by the library go through the static functions of this class.
 *
 * @author zsh
 */
public class ExpressionError {
    private ExpressionError() {
    }

    /**
     * <p>
     * Create an error message based on the provided message and stream, highlighting the line on which the error happened. If the
     * stream has more tokens, the next token will be highlighted. Otherwise the end of the source of the stream will be
     * highlighted.
     * </p>
     *
     * <p>
     * Throws a {@link RuntimeException}
     * </p>
     */
    public static TemplateException error(TokenStream stream, String message, Object[] args) {
        String errorMsg = String.format(message, args);
        if (stream.hasMore()) {
            return error(errorMsg, stream.consume().getSpan());
        } else {
            String source = stream.getSource();
            if (source == null) {
                return error(errorMsg, new Span(" ", 0, 1));
            } else {
                return error(errorMsg, new Span(source, source.length() - 1, source.length()));
            }
        }
    }

    public static TemplateException error(TokenStream stream, String message) {
        return error(stream, message, new Object[0]);
    }

    /**
     * Create an error message based on the provided message and location, highlighting the location in the line on which the
     * error happened. Throws a {@link TemplateException}
     *
     * @return TemplateException
     */
    public static TemplateException error(String message, Span location, Throwable cause) {
        Line line = location.getLine();
        String errorTmp = "Error (%d): %s\n\n%s\n";
        String preMsg = String.format(errorTmp, line.getLineNumber(), message, line.getText());
        StringBuilder errorMessage = new StringBuilder(preMsg);
        int errorStart = location.getStart() - line.getStart();
        int errorEnd = errorStart + location.getText().length() - 1;
        for (int i = 0, n = line.getText().length(); i < n; i++) {
            String useTab = line.getText().charAt(i) == '\t' ? "\t" : " ";
            errorMessage.append(i >= errorStart && i <= errorEnd ? "^" : useTab);
        }
        String msg = errorMessage.toString();
        if (cause == null) {
            return new TemplateException(msg, location);
        } else {
            return new TemplateException(msg, location, cause);
        }
    }

    /**
     * Create an error message based on the provided message and location, highlighting the location in the line on which the
     * error happened. Throws a {@link TemplateException}
     **/
    public static TemplateException error(String message, Span location) {
        return error(message, location, null);
    }
}
